package type_builder

import baml "github.com/boundaryml/baml/engine/language_client_go/pkg"

type Type = baml.Type

type llmViewOnly interface {
	Description() (*string, error)
	Alias() (*string, error)
	From() (baml.ASTNodeSource, error)
	Name() (string, error)
}

type llmWriteOnly interface {
	SetDescription(description string) error
	SetAlias(alias string) error
}

type enumBase interface {
	llmViewOnly
	Type() (Type, error)
}

type EnumView interface {
	enumBase
	ListValues() ([]EnumValueView, error)
	Value(name string) (EnumValueView, error)
}

type EnumBuilder interface {
	enumBase
	llmWriteOnly
	ListValues() ([]EnumValueBuilder, error)
	AddValue(value string) (EnumValueBuilder, error)
	Value(name string) (EnumValueBuilder, error)
}

type EnumValueView interface {
	llmViewOnly
}

type EnumValueBuilder interface {
	EnumValueView
	llmWriteOnly
	SetSkip(skip bool) error
}

type classBase interface {
	llmViewOnly
	Type() (Type, error)
}

type ClassView interface {
	classBase
	ListProperties() ([]ClassPropertyView, error)
	Property(name string) (ClassPropertyView, error)
}

type ClassBuilder interface {
	classBase
	llmWriteOnly
	ListProperties() ([]ClassPropertyBuilder, error)
	AddProperty(name string, fieldType Type) (ClassPropertyBuilder, error)
	Property(name string) (ClassPropertyBuilder, error)
}

type ClassPropertyView interface {
	llmViewOnly
	Type() (Type, error)
}

type ClassPropertyBuilder interface {
	ClassPropertyView
	llmWriteOnly
	SetType(fieldType Type) error
}

type TypeBuilder struct {
	inner baml.TypeBuilder
}

func (t *TypeBuilder) InternalExport() baml.TypeBuilder {
	return t.inner
}

func InternalNewTypeBuilder(inner baml.TypeBuilder) *TypeBuilder {
	return &TypeBuilder{inner: inner}
}

func (t *TypeBuilder) String() (Type, error) {
	return t.inner.String()
}

func (t *TypeBuilder) Int() (Type, error) {
	return t.inner.Int()
}

func (t *TypeBuilder) Float() (Type, error) {
	return t.inner.Float()
}

func (t *TypeBuilder) Bool() (Type, error) {
	return t.inner.Bool()
}

func (t *TypeBuilder) Null() (Type, error) {
	return t.inner.Null()
}

func (t *TypeBuilder) LiteralString(value string) (Type, error) {
	return t.inner.LiteralString(value)
}

func (t *TypeBuilder) LiteralInt(value int64) (Type, error) {
	return t.inner.LiteralInt(value)
}

func (t *TypeBuilder) LiteralBool(value bool) (Type, error) {
	return t.inner.LiteralBool(value)
}

func (t *TypeBuilder) Map(key Type, value baml.Type) (Type, error) {
	return t.inner.Map(key, value)
}

func (t *TypeBuilder) List(inner baml.Type) (Type, error) {
	return t.inner.List(inner)
}

func (t *TypeBuilder) Optional(inner baml.Type) (Type, error) {
	return t.inner.Optional(inner)
}

func (t *TypeBuilder) Union(types []baml.Type) (Type, error) {
	return t.inner.Union(types)
}

func (t *TypeBuilder) AddBaml(baml string) error {
	return t.inner.AddBaml(baml)
}

func (t *TypeBuilder) AddEnum(name string) (EnumBuilder, error) {
	bld, err := t.inner.AddEnum(name)
	if err != nil {
		return nil, err
	}
	return &dynamicEnumBuilder{EnumBuilder: bld}, nil
}

func (t *TypeBuilder) AddClass(name string) (ClassBuilder, error) {
	bld, err := t.inner.AddClass(name)
	if err != nil {
		return nil, err
	}
	return &dynamicClassBuilder{ClassBuilder: bld}, nil
}

// override the default stringer to use the type builder's print method
func (t *TypeBuilder) Format(f fmt.State, verb rune) {
	display := t.inner.Print()
	fmt.Fprint(f, display)
}

// ----------------------------------------------------------------------------
// Dynamic builders: Enums (enums defined purely in the type builder)
// ----------------------------------------------------------------------------

type dynamicEnumBuilder struct {
	baml.EnumBuilder
}

func (t dynamicEnumBuilder) AddValue(value string) (EnumValueBuilder, error) {
	return t.EnumBuilder.AddValue(value)
}

func (t dynamicEnumBuilder) ListValues() ([]EnumValueBuilder, error) {
	values, err := t.EnumBuilder.ListValues()
	if err != nil {
		return nil, err
	}
	builders := make([]EnumValueBuilder, len(values))
	for i, v := range values {
		builders[i] = v
	}
	return builders, nil
}

func (t dynamicEnumBuilder) Value(name string) (EnumValueBuilder, error) {
	return t.EnumBuilder.Value(name)
}

// ----------------------------------------------------------------------------
// Dynamic builders: Classes (classes defined purely in the type builder)
// ----------------------------------------------------------------------------

type dynamicClassBuilder struct {
	baml.ClassBuilder
}

func (t dynamicClassBuilder) AddProperty(name string, fieldType Type) (ClassPropertyBuilder, error) {
	return t.ClassBuilder.AddProperty(name, fieldType)
}

func (t dynamicClassBuilder) ListProperties() ([]ClassPropertyBuilder, error) {
	properties, err := t.ClassBuilder.ListProperties()
	if err != nil {
		return nil, err
	}
	builders := make([]ClassPropertyBuilder, len(properties))
	for i, p := range properties {
		builders[i] = p
	}
	return builders, nil
}

func (t dynamicClassBuilder) Property(name string) (ClassPropertyBuilder, error) {
	return t.ClassBuilder.Property(name)
}